#include "stdafx.h"
#include "Node.h"

using namespace std;

namespace FEngine
{
	Node::Node()
	{
		mParentNode				= NULL;
		mPosition				= Vector3(0.0f, 0.0f, 0.0f);
		mScale					= Vector3(1.0f, 1.0f, 1.0f);
		mLocalScale				= Vector3(1.0f, 1.0f, 1.0f);
		mRotation				= Vector3(0.0f, 0.0f, 0.0f);
		mHasLocalMatrixChanged	= true;
		mHasWorldMatrixChanged	= true;
		D3DXMatrixIdentity(&mWorldMatrix);
		D3DXMatrixIdentity(&mLocalMatrix);
	}

	Node::~Node()
	{
		if (mParentNode != NULL)
			mParentNode->removeChild(this);

		for (list<Node*>::iterator it = mChildNodeList.begin(); it != mChildNodeList.end(); ++it)
			(*it)->_setParentNode(NULL);
	}
	
	void Node::addChild(Node* _node)
	{
		if (_node == this)
			return;

		mChildNodeList.push_back(_node);
		_node->_setParentNode(this);
		_node->_addedToParentNode();
	}

	void Node::_addedToParentNode()
	{
		_notifyWorldMatrixChanged();
	}
	
	void Node::removeChild(Node* _node)
	{
		mChildNodeList.remove(_node);
		_node->_setParentNode(NULL);
		_node->_notifyWorldMatrixChanged();
	}

	void Node::_setParentNode(Node* _node)
	{
		mParentNode = _node;
	}
	
	Vector3 Node::getPositionWorld()
	{
		Matrix4 mat = _getWorldMatrix();
		return Vector3(mat._41, mat._42, mat._43);
	}
	
	void Node::setPosition(const Vector3& _pos)
	{
		mPosition = _pos;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	void Node::setPosition(float _x, float _y, float _z)
	{
		mPosition.x = _x;
		mPosition.y = _y;
		mPosition.z = _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::translate(const Vector3& _pos)
	{
		mPosition += _pos;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	void Node::translate(float _x, float _y, float _z)
	{
		mPosition.x += _x;
		mPosition.y += _y;
		mPosition.z += _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::setScale(const Vector3& _scale)
	{
		mScale = _scale;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::setScale(float _x, float _y, float _z)
	{
		mScale.x = _x;
		mScale.y = _y;
		mScale.z = _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::setScale(float _scale)
	{
		mScale = mScale * _scale;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::scale(const Vector3& _scale)
	{
		mScale.x *= _scale.x;
		mScale.y *= _scale.y;
		mScale.z *= _scale.z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::scale(float _x, float _y, float _z)
	{
		mScale.x *= _x;
		mScale.y *= _y;
		mScale.z *= _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::scale(float _scale)
	{
		mScale.x *= _scale;
		mScale.y *= _scale;
		mScale.z *= _scale;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}
	
	void Node::setLocalScale(const Vector3& _scale)
	{
		mLocalScale = _scale;
	}
	
	void Node::setLocalScale(float _x, float _y, float _z)
	{
		mLocalScale.x = _x;
		mLocalScale.y = _y;
		mLocalScale.z = _z;
	}
	
	void Node::setLocalScale(float _scale)
	{
		mLocalScale *= _scale;
	}

	void Node::setRotation(const Vector3& _rot)
	{
		mRotation = _rot;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	void Node::setRotation(float _x, float _y, float _z)
	{
		mRotation.x = _x;
		mRotation.y = _y;
		mRotation.z = _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	void Node::rotate(const Vector3& _rot)
	{
		mRotation += _rot;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	void Node::rotate(float _x, float _y, float _z)
	{
		mRotation.x += _x;
		mRotation.y += _y;
		mRotation.z += _z;
		mHasLocalMatrixChanged = true;
		_notifyWorldMatrixChanged();
	}

	Matrix4 Node::_getWorldMatrix()
	{
		if (mHasLocalMatrixChanged)
			_calculateLocalMatrix();

		if (mHasWorldMatrixChanged)
			_calculateWorldMatrix();

		return mWorldMatrix;
	}

	void Node::update(double _elapsedTime)
	{
		for (list<Node*>::iterator it = mChildNodeList.begin(); it != mChildNodeList.end(); ++it)
			(*it)->update(_elapsedTime);
	}

	void Node::render(double _elapsedTime)
	{
		for (list<Node*>::iterator it = mChildNodeList.begin(); it != mChildNodeList.end(); ++it)
			(*it)->render(_elapsedTime);
	}
	
	void Node::_calculateLocalMatrix()
	{
		Matrix4 matTranslation;
		D3DXMatrixTranslation(&matTranslation, mPosition.x, mPosition.y, mPosition.z);
		
		Matrix4 matScale;
		D3DXMatrixScaling(&matScale, mScale.x, mScale.y, mScale.z);
		
		Matrix4 matRot;
		D3DXMatrixRotationYawPitchRoll(&matRot, mRotation.z, mRotation.x, mRotation.y);
		
		D3DXMatrixMultiply(&mLocalMatrix, &matScale, &matRot);
		D3DXMatrixMultiply(&mLocalMatrix, &mLocalMatrix, &matTranslation);
		
		mHasLocalMatrixChanged = false;
		_notifyWorldMatrixChanged();
	}
	
	void Node::_calculateWorldMatrix()
	{
		mWorldMatrix = mLocalMatrix;
		
		Matrix4 parentMat;
		if (mParentNode != NULL)
			parentMat = mParentNode->_getWorldMatrix();
		else
			D3DXMatrixIdentity(&parentMat);
		
		D3DXMatrixMultiply(&mWorldMatrix, &mWorldMatrix, &parentMat);

		mHasWorldMatrixChanged = false;
	}
	
	void Node::_notifyWorldMatrixChanged()
	{
		mHasWorldMatrixChanged = true;
		for (list<Node*>::iterator it = mChildNodeList.begin(); it != mChildNodeList.end(); ++it)
			(*it)->_notifyWorldMatrixChanged();
	}

/*
string Node::_printTree()
{
	string str = "";
	if (mParentNode != NULL)
		str += mParentNode->getName() + "->";
	str += mName + " ";
	for (list<Node*>::iterator it = mChildNodeList.begin(); it != mChildNodeList.end(); ++it)
		str += (*it)->_printTree();
	return str;
}*/

} // namespace FEngine
